import { _decorator, animation, Camera, CapsuleCharacterController, CharacterControllerContact, clamp, geometry, Node, PhysicsSystem, Quat, random, RigidBody, v2, v3, Vec2, Vec3 } from 'cc';
import { GamerController } from './GamerController';
import { Gun } from './Items/Gun';
import { Heal } from './Items/Heal';
import { Armor } from './Items/Armor';
import { Melee } from './Items/Melee';
import { Pack } from './Items/Pack';
import { Ammo } from './Items/Ammo';
import { Module } from './Items/Module';
import { BodyTrigger } from './BodyTrigger';
import { InGameManager } from './Managers/InGameManager';
import { ControlUI } from './UIs/ControlUI';
import { PoolManager } from '../../Scripts/Framework/Managers/PoolManager';
import NodeUtil from '../../Scripts/Framework/Utils/NodeUtil';
import { GameDataManager } from '../../MainPack/Scripts/GameDataManager';
const { ccclass, property } = _decorator;

@ccclass('PlayerController')
export class PlayerController extends GamerController {

    private static instance: PlayerController;
    public static get Instance(): PlayerController {
        return this.instance;
    }

    type = 'PlayerController' as const;

    @property(Camera)
    UICamera: Camera;

    @property(Camera)
    playerCamera: Camera;

    public get Hp(): number {
        return this.hp;
    }
    public set Hp(value: number) {
        if (this.isDead) return;
        value = clamp(value, 0, 100);
        if (value > this.hp) InGameManager.Instance.endData[3] += value - this.hp;
        else if (value < this.hp) InGameManager.Instance.endData[5] -= value - this.hp;
        this.hp = value;
        if (this.hp == 0) this.Die();
        ControlUI.Instance.hp.fillRange = this.hp / 100;
        ControlUI.Instance._HpChange();
    }

    isParachute: boolean = true;

    protected onLoad(): void {
        PlayerController.instance = this;
        super.onLoad();
        // Events.once(EventName.JsonCompleted, this.TestInit, this);
    }

    start() {
        super.start();
        this.InitModel();
        this.backPack.Init();
        // this.TestInit();
        this.isInit = true;
    }

    update(deltaTime: number) {
        super.update(deltaTime);
        this.Move(deltaTime);
        this._InHeal(deltaTime);
        this.Auto();
    }

    InitModel(): void {
        let parent = this.node.children[0];
        if (parent.children[0]) PoolManager.PutNode(parent.children[0]);
        let data = GameDataManager.Instance.getArrayData("UseSkin");
        let model: Node = PoolManager.GetNodeByPrefab(InGameManager.Instance.gamerPrefabs[data[0]], parent);
        model.layer = 1 << 5;
        let triggers = model.getComponentsInChildren(BodyTrigger);
        triggers.forEach((value) => {
            value.controller = this;
            value.getComponent(RigidBody).group = 1 << 5;
        });
        this.headwears = NodeUtil.GetNode("toutao", model);
        this.wings = NodeUtil.GetNode("chibang", model);
        // this.beibao = NodeUtil.GetNode("beibao", model);
        this.wings.active = true;
        // this.beibao.active = false;
        if (this.headwears.children[0]) PoolManager.PutNode(this.headwears.children[0]);
        if (this.wings.children[0]) PoolManager.PutNode(this.wings.children[0]);
        if (data[1] >= 0) {
            let head = InGameManager.itemsJson.Adorn[1][data[1]];
            PoolManager.GetNodeByBundle("MainPack", head.prefab, this.headwears);
        }
        if (data[2] >= 0) {
            let wing = InGameManager.itemsJson.Adorn[2][data[2]];
            PoolManager.GetNodeByBundle("MainPack", wing.prefab, this.wings);
        }
        this.SetLayer(model);
        this.animationController = this.node.children[0].children[0].getComponent(animation.AnimationController);
        this.animationController.setValue("IsDead", false);
    }

    SetLayer(target: Node) {
        for (let i = 0; i < target.children.length; i++) {
            const element = target.children[i];
            element.layer = 1 << 5;
            if (element.children.length > 0) this.SetLayer(element);
        }
    }

    TestInit() {
        // this.backPack.GetItem(Module.Init(3, 0));
        // this.backPack.GetItem(Pack.Init(2));
        // this.backPack.GetItem(Heal.Init(0, 1));
        // this.backPack.GetItem(Heal.Init(1, 2));
        // this.backPack.GetItem(Heal.Init(2, 3));
        // this.backPack.GetItem(Heal.Init(3, 4));
        // this.backPack.GetItem(Heal.Init(4, 5));
        this.backPack.GetItem(Armor.Init(0));
        this.backPack.GetItem(Armor.Init(3));
        // this.backPack.GetItem(Melee.Init(2));
        this.backPack.GetItem(Gun.Init(1, 0));
        // this.backPack.GetItem(Gun.Init(1, 2));
        // this.backPack.GetItem(Gun.Init(2, 0));
        this.backPack.GetItem(Ammo.Init(1));
        // this.backPack.GetItem(Ammo.Init(4));
        // this.backPack.GetItem(Ammo.Init(2));
        // this.backPack.GetItem(Ammo.Init(3));
        // this.backPack.GetItem(Ammo.Init(4));
        // this.backPack.GetItem(Module.Init(0, 0));
        // this.schedule(() => { ControlUI.Instance._Hurt(this.target.getWorldPosition()) }, 1);
    }

    Die() {
        super.Die();
        //玩家死亡
    }

    Auto() {
        if (!ControlUI.Instance.isAuto) return;
        let uiPos = this.UICamera.worldToScreen(ControlUI.Instance.frontSight.getWorldPosition());
        let ray = this.playerCamera.screenPointToRay(uiPos.x, uiPos.y);
        if (PhysicsSystem.instance.raycastClosest(ray, 1 << 0 | 1 << 2, 1000)) {
            let result = PhysicsSystem.instance.raycastClosestResult;
            if (result.collider.attachedRigidBody?.group == 1 << 2) {
                if (!this.animationController.getValue("IsShoot")) this.ShootStart();
            }
            else {
                if (this.animationController.getValue("IsShoot")) this.ShootEnd();
            }
        }
        else {
            if (this.animationController.getValue("IsShoot")) this.ShootEnd();
        }
    }

    ShootBullet(): void {
        let oPos = this.backPack.weaponSlots[4].children[0].children[0].getWorldPosition();
        let dPos = v3();
        let uiPos = this.UICamera.worldToScreen(ControlUI.Instance.frontSight.getWorldPosition());
        let offset = random() * ControlUI.Instance.frontSight.children[1].x;
        let gun = this.backPack.CurWeapon;
        if (gun instanceof Gun && gun.type == 2 && ControlUI.Instance.isAim) offset = 0;
        let offPos = v2();
        Vec2.random(offPos, offset);
        uiPos.add3f(offPos.x, offPos.y, 0);
        let ray = this.playerCamera.screenPointToRay(uiPos.x, uiPos.y);
        if (PhysicsSystem.instance.raycastClosest(ray, 1 << 0 | 1 << 2, 1000)) this.CheckHit(ray, oPos);
        else {
            let o2 = this.playerCamera.node.getWorldPosition();
            let forward = this.playerCamera.node.forward.clone();
            let up = this.playerCamera.node.up.clone();
            let right = this.playerCamera.node.right.clone();
            let dir = this.getRandomShootDirectionSimple(forward, up, right);
            Vec3.add(dPos, o2, dir.multiplyScalar(1000));
            geometry.Ray.fromPoints(ray, o2, dPos);
            this.CheckHit(ray, oPos);
        }
    }

    CheckHit(ray: geometry.Ray, oPos: Vec3) {
        let pos = oPos;
        if (PhysicsSystem.instance.raycastClosest(ray, 1 << 0 | 1 << 2, 1000)) {
            const result = PhysicsSystem.instance.raycastClosestResult;
            const point = result.hitPoint.clone();
            InGameManager.Instance.ShowTrail(pos, point);
            const rig = result.collider.attachedRigidBody;
            if (rig && rig.group == 1 << 2) {
                let body = rig.getComponent(BodyTrigger);
                body.Damage(this.backPack.CurWeapon, this);
                if (body.type != 2) InGameManager.Instance.ShowBlood(point);
                else InGameManager.Instance.ShowFirework(point);
                ControlUI.Instance._Hit();
            }
        }
        else InGameManager.Instance.ShowTrail(pos, pos.clone().add(ray.d.clone().multiplyScalar(1000)));
    }

    MeleeAtk() {
        let ray = new geometry.Ray;
        let v = v3();
        this.UICamera.worldToScreen(ControlUI.Instance.frontSight.getWorldPosition(), v);
        this.playerCamera.screenPointToRay(v.x, v.y, ray);
        if (PhysicsSystem.instance.raycastClosest(ray, 1 << 0 | 1 << 2, 10)) {
            const result = PhysicsSystem.instance.raycastClosestResult;
            const body = result.collider.getComponent(BodyTrigger);
            if (body) {
                let targetPos = body.controller.node.getWorldPosition();
                let pos = this.node.getWorldPosition();
                let dis = Vec3.distance(targetPos, pos);
                let range = 2;
                if (this.backPack.CurWeapon instanceof Melee) range = this.backPack.CurWeapon.range;
                if (dis < range) {
                    body.Damage(this.backPack.CurWeapon, this);
                    const point = result.hitPoint.clone();
                    if (body.type != 2) InGameManager.Instance.ShowBlood(point);
                    else InGameManager.Instance.ShowFirework(point);
                    ControlUI.Instance._Hit();
                }
            }
        }
    }

}